//
//  Copyright (C) 2001,2002 HorizonLive.com, Inc.  All Rights Reserved.
//  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
//
//  This is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This software is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this software; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
//  USA.
//

package name.elftesting.rfbplayer;

import java.awt.*;
import java.awt.event.*;
import java.io.*;

public class RfbPlayer extends java.applet.Applet implements
		java.lang.Runnable, WindowListener {

	public boolean inAnApplet = true;
	public boolean inSeparateFrame = false;

	/** current applet width */
	int dispW = 300;
	/** current applet height */
	int dispH = 200;

	//
	// main() is called when run as a java program from the command line.
	// It simply runs the applet inside a newly-created frame.
	//
	public static void main(String[] argv) {
		argv=new String[]{"URL","file:///d:/workspace/vncelf/vncsession.002.fbs"};
		RfbPlayer p = new RfbPlayer();
		p.mainArgs = argv;
		p.inAnApplet = false;
		p.inSeparateFrame = true;

		p.init();
		p.start();
	}

	public void player(String[] argv) {
		RfbPlayer p = new RfbPlayer();
		p.mainArgs = argv;
		p.inAnApplet = false;
		p.inSeparateFrame = true;

		p.init();
		p.start();
	}

	public String[] mainArgs;

	FbsInputStream fbs;
	RfbProto rfb;
	Thread rfbThread;

	Frame vncFrame;
	Container vncContainer;

	LWScrollPane desktopScrollPane;

	BorderLayout borderLayout;
	ButtonPanel buttonPanel;
	VncCanvas vc;

	String sessionURL;
	String idxPrefix;
	long initialTimeOffset;
	double playbackSpeed;
	boolean autoPlay;
	boolean showControls;
	boolean isQuitting = false;
	int deferScreenUpdates;

	
	public void init() {

		// LiveConnect work-a-round
		RfbSharedStatic.refApplet = this;

		readParameters();

		if (inSeparateFrame) {
			vncFrame = new Frame("RFB Session Player");
			if (!inAnApplet) {
				vncFrame.add("Center", this);
			}
			vncContainer = vncFrame;
		} else {
			vncContainer = this;
		}

		if (inSeparateFrame)
			vncFrame.addWindowListener(this);

		rfbThread = new Thread(this, "RfbThread");
		rfbThread.start();
	}

	public void update(Graphics g) {
	}

	//
	// run() - executed by the rfbThread to read RFB data.
	//
	public void run() {



		borderLayout=new BorderLayout();
		vncContainer.setLayout(borderLayout);
		GridBagConstraints gbc = new GridBagConstraints();


		gbc.fill = GridBagConstraints.BOTH;
		gbc.anchor=GridBagConstraints.SOUTH;
		if (showControls) {
			buttonPanel = new ButtonPanel(this);
			buttonPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
	
			vncContainer.add(buttonPanel,BorderLayout.SOUTH);
		
		}
		

		if (inSeparateFrame) {
			vncFrame.pack();
			vncFrame.setVisible(true);
		} else {
			validate();
		}

		try {
			java.applet.Applet applet = (inAnApplet) ? this : null;
			FbsConnection conn = new FbsConnection(sessionURL, idxPrefix,
					applet);
			fbs = conn.connect(initialTimeOffset);
			
			rfb = new RfbProto(fbs);
		
			System.out.println(fbs.getTimeOffset());
			vc = new VncCanvas(this);
			

			// Create a panel which itself is resizeable and can hold
			// non-resizeable VncCanvas component at the top left corner.
			// Panel canvasPanel = new Panel();
			// canvasPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
			// canvasPanel.add(vc);

			// Create a ScrollPane which will hold a panel with VncCanvas
			// inside.
			// desktopScrollPane = new
			// ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED);
			desktopScrollPane = new LWScrollPane();
				
			desktopScrollPane.addComp(vc);
			desktopScrollPane.setSize(dispW, dispH);

			
		
			// Now add the scroll bar to the container.
			if (inSeparateFrame) {
				
				vncFrame.add(desktopScrollPane,BorderLayout.NORTH);
				vncFrame.setTitle(rfb.desktopName);
				vc.resizeDesktopFrame();
			} else {
				// Size the scroll pane to display size.
				desktopScrollPane.setSize(dispW, dispH);

				// Just add the VncCanvas component to the Applet.
				gbc.fill = GridBagConstraints.NONE;
				//gridbag.setConstraints(desktopScrollPane, gbc);
				add(desktopScrollPane);
				validate();
				vc.resizeEmbeddedApplet();
			}
	
		
			
			while (!isQuitting) {
				try {
					setPaused(!autoPlay);
					fbs.setSpeed(playbackSpeed);
					vc.processNormalProtocol();
				} catch (EOFException e) {
					long newTimeOffset;
					if (e.getMessage() != null
							&& e.getMessage().equals("[JUMP]")) {
						// A special type of EOFException allowing us to close
						// FBS stream
						// and then re-open it for jumping to a different time
						// offset.
						newTimeOffset = fbs.getSeekOffset();
						autoPlay = !fbs.isPaused();
					} else {
						// Return to the beginning after the playback is
						// finished.
						newTimeOffset = 0;
						autoPlay = false;
					}
					fbs.close();
					fbs = conn.connect(newTimeOffset);
					rfb.newSession(fbs);
					vc.updateFramebufferSize();
				} catch (NullPointerException e) {
					// catching this causes a hang with 1.4.1 JVM's under Win32
					// IE
					throw e;
				}
			}

		} catch (FileNotFoundException e) {
			fatalError(e.toString());
		} catch (Exception e) {
			e.printStackTrace();
			fatalError(e.toString());
		}

	}

	public void setPausedInt(String paused) {
		// default to true (pause)
		int pause = 1;

		try {
			pause = Integer.parseInt(paused);
		} catch (NumberFormatException e) {
		}

		if (pause == 0) {
			setPaused(false);
		} else {
			setPaused(true);
		}
	}

	public void setPaused(boolean paused) {
		if (showControls)
			buttonPanel.setPaused(paused);
		if (paused) {
			fbs.pausePlayback();
		} else {
			fbs.resumePlayback();
		}
	}

	public double getSpeed() {
		return playbackSpeed;
	}

	public void setSpeed(double speed) {
		playbackSpeed = speed;
		fbs.setSpeed(speed);
	}

	public void jumpTo(long pos) {
		long diff = Math.abs(pos - fbs.getTimeOffset());

		// Current threshold is 5 seconds
		if (diff > 5000) {
			fbs.pausePlayback();
			setPos(pos);
			fbs.resumePlayback();
		}
	}

	public void setPos(long pos) {
		fbs.setTimeOffset(pos, true);
	}

	public void updatePos() {
		if (showControls && buttonPanel != null)
			buttonPanel.setPos(fbs.getTimeOffset());
	}

	//
	// readParameters() - read parameters from the html source or from the
	// command line. On the command line, the arguments are just a sequence of
	// param_name/param_value pairs where the names and values correspond to
	// those expected in the html applet tag source.
	//
	public void readParameters() {

		sessionURL = readParameter("URL", true);
		idxPrefix = readParameter("Index", false);

		initialTimeOffset = readLongParameter("Position", 0);
		if (initialTimeOffset < 0)
			initialTimeOffset = 0;

		playbackSpeed = readDoubleParameter("Speed", 1.0);
		if (playbackSpeed <= 0.0)
			playbackSpeed = 1.0;

		autoPlay = false;
		String str = readParameter("Autoplay", false);
		if (str != null && str.equalsIgnoreCase("Yes"))
			autoPlay = true;

		showControls = true;
		str = readParameter("Show_Controls", false);
		if (str != null && str.equalsIgnoreCase("No"))
			showControls = false;

		if (inAnApplet) {
			str = readParameter("Open_New_Window", false);
			if (str != null && str.equalsIgnoreCase("Yes"))
				inSeparateFrame = true;
		}

		// Fine tuning options.
		deferScreenUpdates = (int) readLongParameter("Defer_screen_updates", 20);
		if (deferScreenUpdates < 0)
			deferScreenUpdates = 0; // Just in case.

		// Display width and height.
		dispW = readIntParameter("DISPLAY_WIDTH", dispW);
		dispH = readIntParameter("DISPLAY_HEIGHT", dispH);
	}

	public String readParameter(String name, boolean required) {
		if (inAnApplet) {
			String s = getParameter(name);
			if ((s == null) && required) {
				fatalError(name + " parameter not specified");
			}
			return s;
		}

		for (int i = 0; i < mainArgs.length; i += 2) {
			if (mainArgs[i].equalsIgnoreCase(name)) {
				try {
					return mainArgs[i + 1];
				} catch (Exception e) {
					if (required) {
						fatalError(name + " parameter not specified");
					}
					return null;
				}
			}
		}
		if (required) {
			fatalError(name + " parameter not specified");
		}
		return null;
	}

	long readLongParameter(String name, long defaultValue) {
		String str = readParameter(name, false);
		long result = defaultValue;
		if (str != null) {
			try {
				result = Long.parseLong(str);
			} catch (NumberFormatException e) {
			}
		}
		return result;
	}

	double readDoubleParameter(String name, double defaultValue) {
		String str = readParameter(name, false);
		double result = defaultValue;
		if (str != null) {
			try {
				result = Double.valueOf(str).doubleValue();
			} catch (NumberFormatException e) {
			}
		}
		return result;
	}

	int readIntParameter(String name, int defaultValue) {
		String str = readParameter(name, false);
		int result = defaultValue;
		if (str != null) {
			try {
				result = Integer.parseInt(str);
			} catch (NumberFormatException e) {
			}
		}
		return result;
	}

	//
	// fatalError() - print out a fatal error message.
	//
	public void fatalError(String str) {
		System.err.println(str);

		if (inAnApplet) {
			vncContainer.removeAll();
			if (rfb != null) {
				rfb = null;
			}
			Label errLabel = new Label(str);
			errLabel.setFont(new Font("Helvetica", Font.PLAIN, 12));
			vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30));
			vncContainer.add(errLabel);
			if (inSeparateFrame) {
				vncFrame.pack();
			} else {
				validate();
			}
			Thread.currentThread().stop();
		} 
//		
//		else {
//			System.exit(1);
//		}
	}

	//
	// This method is called before the applet is destroyed.
	//
	public void destroy() {
		isQuitting = true;
		vncContainer.removeAll();
		if (fbs != null) {
			fbs.quit();
			try {
				fbs.close();
			} catch (IOException e) {
			}
		}
		try {
			rfbThread.join();
		} catch (InterruptedException e) {
		}
		if (inSeparateFrame) {
			vncFrame.removeWindowListener(this);
			vncFrame.dispose();
		}
	}

	//
	// Set the new width and height of the applet. Call when browser is resized
	// to
	// resize the viewer.
	//
	public void displaySize(int width, int height) {
		dispW = width;
		dispH = height;
		if (!inSeparateFrame) {
			vc.resizeEmbeddedApplet();
		}
	}

	//
	// Close application properly on window close event.
	//
	public void windowClosing(WindowEvent evt) {
		vncContainer.removeAll();
		if (rfb != null)
			rfb = null;

		vncFrame.dispose();
//		if (!inAnApplet) {
//			System.exit(0);
//		}
	}

	//
	// Ignore window events we're not interested in.
	//
	public void windowActivated(WindowEvent evt) {
	}

	public void windowDeactivated(WindowEvent evt) {
	}

	public void windowOpened(WindowEvent evt) {
	}

	public void windowClosed(WindowEvent evt) {
	}

	public void windowIconified(WindowEvent evt) {
	}

	public void windowDeiconified(WindowEvent evt) {
	}

}
